/*
 * CRtmpMediaCaster.cpp
 *
 *  Created on: 2016年9月15日
 *      Author: zhengboyuan
 */

#include "CRtmpMediaCaster.h"
#include "CLog.h"
#include "TSafeStr.h"
#include "H264PropParser.h"


namespace av
{

CRtmpMediaCaster::CRtmpMediaCaster():
		m_id(),
		m_eventCallback(),
		m_eventContext()
{
	m_sink.setEventSink(this);
}

CRtmpMediaCaster::~CRtmpMediaCaster()
{
}

int CRtmpMediaCaster::open(const MediaFormat& fmt, const std::string& url)
{
	if (fmt.hasVideo() && (fmt.m_codec != MEDIA_CODEC_H264))
	{
		return EINVAL;
	}

	m_url = url;
	m_format = fmt;

	m_sink.open(url);

	if (checkFormatReady())
	{
		m_sink.onMediaFormat(fmt);
		//m_writer.open(m_url, m_format);
	}

	return 0;
}

void CRtmpMediaCaster::close()
{
	m_sink.close();
}

bool CRtmpMediaCaster::isOpen()
{
	return m_sink.isOpen();
}



void CRtmpMediaCaster::write(MediaPacketPtr& pkt)
{
	comn::AutoCritSec lock(m_cs);

	if (!filter(pkt))
	{
		return;
	}

	m_sink.onMediaPacket(pkt);
}

void CRtmpMediaCaster::setCallback(CasterEventCallback cb, void* context)
{
	comn::AutoCritSec lock(m_cs);
	m_eventCallback = cb;
	m_eventContext = context;
}

void CRtmpMediaCaster::setID(caster_t id)
{
	m_id = id;
}

caster_t CRtmpMediaCaster::getID()
{
	return m_id;
}


bool CRtmpMediaCaster::parseParamSet(MediaPacket& pkt)
{
	if (!H264PropParser::startWithH264Code(pkt.data, pkt.size))
	{
		return false;
	}

	uint8_t* data = pkt.data;
	size_t size = pkt.size;

	size_t begin = 0;
	bool found = true;

	while (found)
	{
		size_t pos = H264PropParser::findH264StartCode(data, size, begin + 3);
		if (pos != -1)
		{
			parseH264Nalu(data + begin, pos - begin);
			begin = pos;
		}
		else
		{
			break;
		}
	}

	parseH264Nalu(data + begin, size - begin);

	if (m_sps.size() && m_pps.size())
	{
		std::string sprop;
		av::H264PropParser::joinToSprop(m_sps, m_pps, sprop);
		m_format.m_videoProp.assign(sprop.c_str(), sprop.size());
		return true;
	}

	return false;
}

void CRtmpMediaCaster::parseH264Nalu(const uint8_t* data, size_t length)
{
	NaluPacket nalu;
	if (!H264PropParser::parseNalu(data, length, nalu))
	{
		return;
	}

	if (nalu.type == NaluPacket::NALU_SPS)
	{
		m_sps.assign((char*)nalu.data, nalu.length);
	}
	else if (nalu.type == NaluPacket::NALU_PPS)
	{
		m_pps.assign((char*)nalu.data, nalu.length);
	}
}

bool CRtmpMediaCaster::checkFormatReady()
{
	return m_format.m_videoProp.size() > 0;
}

void CRtmpMediaCaster::onEvent(int type, const std::string& value)
{
	comn::AutoCritSec lock(m_cs);
	if (m_eventCallback)
	{
		CasterEvent event;
		memset(&event, 0, sizeof(event));
		comn::copyStr(event.name, sizeof(event.name), value);
		event.type = type;
		event.handle = m_id;

		(*m_eventCallback)(&event, m_eventContext);
	}
}

bool CRtmpMediaCaster::filter(MediaPacketPtr& pkt)
{
	bool working = true;
	if (pkt->type == MTYPE_VIDEO)
	{
		NaluPacket naluPacket;
		H264PropParser::parseNalu(pkt->data, pkt->size, naluPacket);
		if (naluPacket.type == NaluPacket::NALU_PPS)
		{
			m_pps.assign((char*)naluPacket.data, naluPacket.length);
			m_paramBuffer.write(pkt->data, pkt->size);
			working = false;

			if (!checkFormatReady())
			{
				std::string sprop;
				av::H264PropParser::joinToSprop(m_sps, m_pps, sprop);
				m_format.m_videoProp.assign(sprop.c_str(), sprop.size());

				m_sink.onMediaFormat(m_format);
				//m_writer.open(m_url, m_format);
			}
		}
		else if (naluPacket.type == NaluPacket::NALU_SPS)
		{
			size_t idx = H264PropParser::findH264StartCode(pkt->data, pkt->size, H264PropParser::START_CODE_LENGTH);
			
			if (idx == -1)
			{
				/// 单个的SPS NALU
				m_sps.assign((char*)naluPacket.data, naluPacket.length);
				m_paramBuffer.write(pkt->data, pkt->size);
				working = false;
			}
			else
			{
				m_paramBuffer.clear();

				if (!checkFormatReady())
				{
					if (parseParamSet(*pkt))
					{
						m_sink.onMediaFormat(m_format);
						//m_writer.open(m_url, m_format);
					}
				}
			}
		}
		else if (naluPacket.type == NaluPacket::NALU_SEI)
		{
			m_paramBuffer.write(pkt->data, pkt->size);
			working = false;
		}
		else if (naluPacket.type == NaluPacket::NALU_IFRAME)
		{
			int size = pkt->size + m_paramBuffer.size();

			MediaPacketPtr outPkt(new MediaPacket(size));
			outPkt->write(m_paramBuffer.data(), m_paramBuffer.size());
			outPkt->write(pkt->data, pkt->size);

			outPkt->copyProp(*pkt);

			m_paramBuffer.clear();
		}

	}
	return working;
}




} /* namespace av */
